package com.licode.codekit.buildprj;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.Gray;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBScrollPane;
import com.licode.codekit.db.AssembledDb;
import com.licode.codekit.db.LiSysConfig;
import com.licode.codekit.db.Table;
import com.licode.codekit.modal.CreateModal;
import com.licode.codekit.utils.DbDriverDlUtil;
import com.licode.codekit.utils.LiConfig;
import com.licode.codekit.utils.PomFile;
import com.licode.codekit.utils.UIBuilderUtil;

import javax.swing.*;
import javax.swing.border.LineBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.io.File;
import java.util.ArrayList;

/**
 * @author licode
 */
public class GenerateEntityUiBuilder {
    public static int DEFAULT_WID = 360;
    private final int LABEL_ALIGN = SwingConstants.RIGHT;

    private final JPanel north = new JPanel();
    private final JPanel center = new JPanel();

    private final JLabel authorName = new JLabel("author：", LABEL_ALIGN);
    private final JTextField authorContent = new JTextField("licode");

    private final JLabel dbHost = new JLabel("主机：", LABEL_ALIGN);
    private final JTextField dbHostContent = new JTextField();
    private final JLabel dbPort = new JLabel("端口：", LABEL_ALIGN);
    private final JTextField dbPortContent = new JTextField();
    private final JLabel dbName = new JLabel("数据库：", LABEL_ALIGN);
    private final JTextField dbNameContent = new JTextField();

    private final JLabel dbUser = new JLabel("用户名：", LABEL_ALIGN);
    private final JTextField dbUserContent = new JTextField();
    private final JLabel dbPwd = new JLabel("密码：", LABEL_ALIGN);
    private final JTextField dbPwdContent = new JTextField();
    private final JLabel dbUrl = new JLabel("连接地址：", LABEL_ALIGN);
    private final JTextField dbUrlContent = new JTextField();

    private final JLabel dbDriverName = new JLabel("数据库驱动：", LABEL_ALIGN);
    private final JTextField dbDriverContent = new JTextField();

    private final SpinnerNumberModel spModel = new SpinnerNumberModel();
    private final JSpinner tablePrefixContent = new JSpinner(spModel);
    private final JLabel tablePrefixName = new JLabel("表前缀：", 4);

    private final LiConfig config = new LiConfig();

    private String rootPath;

    private final AssembledDb assembledDb = new AssembledDb();
    private final TableUiBuilder tableUiBuilder = new TableUiBuilder();
    private final UIBuilderUtil uiBuilder = new UIBuilderUtil(76);

    public GenerateEntityUiBuilder() {
    }

    public JPanel initNorth() {
        JLabel title = new JLabel("小码咨询工程助手");
        title.setFont(new Font("微软雅黑", 0, 16));
        title.setHorizontalAlignment(2);
        title.setVerticalAlignment(0);
        // this.north.add(null);
        return this.north;
    }

    private void refreshTables() {
        tableUiBuilder.refreshTables(assembledDb);
    }

    private void intiProjectContext(String dir) {
        String dbPath = LiSysConfig.getDefaultDbConfig();
        if (!getConfig().parse(dbPath)) {
            config.createConfig(dbPath);
        }
        this.rootPath = dir;
    }

    public JComponent initCenter(Project rootPrj, String addPrjDir, DialogWrapper dialogWrapper) {
        int defWidth = 200;
        JButton refreshBtn = this.buildButton("刷新");
        JLabel dbTip = new JLabel("确保数据库驱动存在！");
        JPanel centerRow = new JPanel();
        JPanel rightPanel = new JPanel(new BorderLayout());
        GridLayout layGrid = new GridLayout(0, 1);

        centerRow.setLayout(layGrid);
        layGrid.setHgap(16);

        intiProjectContext(addPrjDir);

        this.center.setLayout(new BorderLayout());

        this.dbPortContent.setMinimumSize(new Dimension(80, 0));

        this.dbDriverContent.setMinimumSize(new Dimension(250, 0));

        this.dbUrlContent.setPreferredSize(new Dimension(DEFAULT_WID, 0));
        this.dbDriverContent.setPreferredSize(new Dimension(DEFAULT_WID, 0));

        centerRow.add(this.buildRow(this.authorName, this.authorContent, defWidth));

        centerRow.add(this.buildRow(this.dbHost, this.dbHostContent, defWidth));
        centerRow.add(this.buildRow(this.dbPort, this.dbPortContent, defWidth));
        centerRow.add(this.buildRow(this.dbName, this.dbNameContent, defWidth));

        centerRow.add(this.buildRow(this.dbUser, this.dbUserContent, defWidth));
        centerRow.add(this.buildRow(this.dbPwd, this.dbPwdContent, defWidth));

        centerRow.add(this.buildRow(this.dbUrl, this.dbUrlContent, defWidth));

        JButton dirBtn = this.buildButton("打开");

        centerRow.add(uiBuilder.buildRow(this.dbDriverName, this.dbDriverContent, dirBtn));

        dbTip.setForeground(JBColor.RED);
        centerRow.add(this.initBottom(rootPrj, dialogWrapper));

        rightPanel.add(this.buildRowWithLeftWid(0, refreshBtn,
                        this.buildRowWithLeftWid(0, this.tablePrefixName, this.tablePrefixContent)),
                BorderLayout.NORTH);
        rightPanel.add(tableUiBuilder.initTablesView(), BorderLayout.CENTER);

        this.center.add(centerRow, BorderLayout.CENTER);
        this.center.add(rightPanel, BorderLayout.EAST);

        setInitValues();

        dirBtn.addActionListener((e) -> {
            JFileChooser fileChooser = new JFileChooser();
            fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            int option = fileChooser.showOpenDialog(this.center);
            if (option == 0) {
                File file = fileChooser.getSelectedFile();
                this.dbDriverContent.setText(file.getAbsolutePath());
            }
        });

        refreshBtn.addActionListener((e) -> {
            refreshTablesFromDb(rootPrj);
        });

        addDbUrlChangeListener();
        doChangeDbUrl();

        handleRefreshTables();
        refreshTables();

        return this.center;
    }

    private void setInitValues() {
        authorContent.setText(getConfig().getAuthor());

        dbNameContent.setText(getConfig().getDbName());
        dbHostContent.setText(getConfig().getDbHost());
        dbPortContent.setText(getConfig().getDbPort());

        dbUserContent.setText(getConfig().getDbUser());
        dbPwdContent.setText(getConfig().getDbPwd());
        dbUrlContent.setText(getConfig().getDbUri());
        dbDriverContent.setText(LiSysConfig.getInstance().getDbDriver());

        this.spModel.setValue(getConfig().getTablePrefix());

        this.dbUrlContent.setEditable(false);
        this.dbDriverContent.setEditable(false);
    }

    private void addDbUrlChangeListener() {
        this.dbHostContent.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }
        });

        this.dbPortContent.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }
        });

        this.dbNameContent.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }
        });
    }

    private JComponent buildRowWithLeftWid(int lWidth, Component left, Component right) {
        left.setMinimumSize(new Dimension(lWidth, 0));
        return uiBuilder.buildFillRow(lWidth, left, right);
    }

    private JComponent buildRow(Component left, Component right, int rWidth) {
        right.setMinimumSize(new Dimension(rWidth, 0));
        right.setMaximumSize(new Dimension(rWidth, 0));
        return uiBuilder.buildFillRow(left, right);
    }

    private JButton buildButton(String text) {
        JButton submit = new JButton(text);
        submit.setHorizontalAlignment(0);
        submit.setVerticalAlignment(0);
        return submit;
    }

    public JComponent initBottom(Project rootPrj, DialogWrapper dialogWrapper) {
        JButton buildEntity = this.buildButton("创建实体");
        JButton cancel = this.buildButton("退出");
        FlowLayout fl = new FlowLayout();
        JPanel panel = new JPanel(fl);
        fl.setAlignment(2);

        panel.add(buildEntity);
        panel.add(cancel);

        buildEntity.addActionListener((e) -> {
            this.doCreateCode(dialogWrapper, rootPrj);
        });

        cancel.addActionListener((e) -> {
            dialogWrapper.close(0);
        });

        return panel;
    }

    private LiConfig getConfig() {
        return this.config;
    }

    private void doChangeDbUrl() {
        String dbFmt = getConfig().getDbUrlTemplate();
        String host = this.dbHostContent.getText();
        String port = this.dbPortContent.getText();
        String name = this.dbNameContent.getText();

        this.dbUrlContent.setText(dbFmt.replace("{host}", host)
                .replace("{port}", port)
                .replace("{name}", name));
    }

    private void refreshTablesFromDb(Project project) {
        String dbUser = this.dbUserContent.getText();
        String dbPwd = this.dbPwdContent.getText();
        String dbUri = this.dbUrlContent.getText();

        if (StringUtil.isEmpty(dbUser)) {
            Messages.showMessageDialog(project, "请输入数据库用户名",
                    "温馨提示", Messages.getInformationIcon());
        } else if (StringUtil.isEmpty(dbPwd)) {
            Messages.showMessageDialog(project, "请输入数据库密码",
                    "温馨提示", Messages.getInformationIcon());
        } else if (StringUtil.isEmpty(dbUri)) {
            Messages.showMessageDialog(project, "请输入数据库连接地址",
                    "温馨提示", Messages.getInformationIcon());
        } else {
            if (!handleRefreshTables()) {
                if (AssembledDb.dbDriverValid()) {
                    Messages.showMessageDialog(project, "打开数据库失败，请检查数据库用户或密码是否正确！",
                            "温馨提示", Messages.getInformationIcon());
                } else {
                    String dbDriverPath = LiSysConfig.getInstance().getDbDriver();
                    if (DbDriverDlUtil.downMysqlFromResource(dbDriverPath, true)) {
                        this.dbDriverContent.setText(dbDriverPath);
                        // 重新初始化数据库
                        AssembledDb.initDbClassloader(dbDriverPath, false);
                        if (!AssembledDb.dbDriverValid()) {
                            Messages.showMessageDialog(project, "初始化失败，数据库驱动无效或不可用！",
                                    "温馨提示", Messages.getInformationIcon());
                        } else if (!handleRefreshTables()) {
                            Messages.showMessageDialog(project, "打开数据库失败，请检查数据库用户或密码是否正确！",
                                    "温馨提示", Messages.getInformationIcon());
                        }
                    } else {
                        Messages.showMessageDialog(project, "数据库驱动缺失，请重新选择！",
                                "温馨提示", Messages.getInformationIcon());
                    }
                }
            }
        }
    }

    private boolean handleRefreshTables() {
        String dbUser = this.dbUserContent.getText();
        String dbPwd = this.dbPwdContent.getText();
        String dbUri = this.dbUrlContent.getText();
        String author = this.authorContent.getText();
        int tabPrefix = this.spModel.getNumber().intValue();
        String host = this.dbHostContent.getText();
        String port = this.dbPortContent.getText();
        String name = this.dbNameContent.getText();

        if (!StringUtil.isEmpty(dbUser) && !StringUtil.isEmpty(dbPwd) &&
                !StringUtil.isEmpty(dbPwd) && !StringUtil.isEmpty(dbUri)) {
            getConfig().setDbName(name);
            getConfig().setDbHost(host);
            getConfig().setDbPort(port);
            getConfig().setDbUser(dbUser);
            getConfig().setDbPwd(dbPwd);
            getConfig().setDbUri(dbUri);
            getConfig().setAuthor(author);
            getConfig().setTablePrefix(tabPrefix);

            getConfig().save();

            if (!org.jsoup.internal.StringUtil.isBlank(name) && this.assembledDb.initTables(dbUri, dbUser, dbPwd)) {
                refreshTables();
                return true;
            }
        }

        return false;
    }

    private void doCreateCode(DialogWrapper dialogWrapper, Project project) {
        java.util.List<Table> tables = tableUiBuilder.getSelectedTable(this.assembledDb);
        if (tables.isEmpty()) {
            Messages.showMessageDialog(project, "请选择数据库表名！",
                    "温馨提示", Messages.getInformationIcon());
        } else {
            this.generateCode(dialogWrapper, project, tables);
        }
    }

    private void generateCode(DialogWrapper dialogWrapper, Project rootPrj,
                              java.util.List<Table> tabs) {
        EntityGenerateBuilder entityBuilder = new EntityGenerateBuilder(rootPath, this.authorContent.getText());
        int tabPrefix = spModel.getNumber().intValue();
        getConfig().setTablePrefix(Math.max(tabPrefix, 0));
        getConfig().save();

        if (entityBuilder.buildEntityClass(rootPrj, tabs, getConfig().getTablePrefix())) {
            Messages.showMessageDialog(rootPrj, "实体创建完成！",
                    "温馨提示", Messages.getInformationIcon());
            dialogWrapper.close(0);
        }
    }
}
